#pragma once

// ref:https://github.com/Dcnqukin/Cpp11_Source_Code
#include <chrono>
#include <thread>
#include <atomic>
#include <memory>
#include <mutex>
#include <condition_variable>
class Timer
{
public:
    Timer() :_stopped(true), _try_to_stop(false) { }
    Timer(const Timer& t)
    {
        _stopped = t._stopped.load();
        _try_to_stop = t._try_to_stop.load();
    }
    ~Timer()
    {
        stop();
    }
    void start(int interval, std::function<void()> task)
    {
        if (_stopped == false)
            return;

        _stopped = false;
        std::thread([this, interval, task]() 
        {
            while (!_try_to_stop)
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(interval));
                task();
            }
            // "stop task..."
            {
                std::lock_guard<std::mutex> locker(_mutex);
                _stopped = true;
                _stop_cond.notify_one();
            }
        }).detach();
    }

    void stop() 
    {
        if (_stopped)
            return;
        if (_try_to_stop) {
            return;
        }
        _try_to_stop = true;
        {
            std::unique_lock<std::mutex> locker(_mutex);
            _stop_cond.wait(locker, [this] {
                return _stopped == true; 
            });
            if (_stopped == true) 
                _try_to_stop = false;
        }
    }

    bool is_stopped()
    {
        return _stopped;
    }

private:
    std::atomic<bool> _stopped;
    std::atomic<bool> _try_to_stop;
    std::mutex _mutex;
    std::condition_variable _stop_cond;
};

